﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Platform.Linq
{
	internal sealed class MapReduceKeyExtractor<TKey> : ExpressionVisitor
	{
		//private Stack<Type> typeContext = new Stack<Type>();
		private Stack<MemberExpression> memberContext = new Stack<MemberExpression>();

		public MapReduceKeyExtractor( Expression keySelectorExpression )
		{
			this.KeySelectorExpression = keySelectorExpression;
		}


		public Expression KeySelectorExpression
		{ get; private set; }

		private List<Func<TKey, object>> gets;

		public List<Func<TKey, object>> ExtractGets()
		{
			this.gets = new List<Func<TKey, object>>();

			// if ( this.KeySelectorExpression is MemberAccessExpression )
			//	this.gets.add( this.KeySelectorExpression.Compile() );
			base.Visit( this.KeySelectorExpression );
			return this.gets;
		}

		protected override ElementInit VisitElementInit( ElementInit node )
		{
			return base.VisitElementInit( node );
		}

		protected override Expression VisitNew( NewExpression node )
		{
			Type keyType = node.Type;
			ParameterExpression keyParam = Expression.Parameter( keyType );

			foreach ( var member in node.Members )
			{
				var getter = Expression.Convert( Expression.MakeMemberAccess( keyParam, member ), typeof( object ) );

				this.gets.Add( Expression.Lambda<Func<TKey, object>>( getter, keyParam ).Compile() );
			}

			return node;
			// return base.VisitNew( node );
			//this.typeContext.Push( node.Type );
			//try
			//{
			//	return base.VisitNew( node );
			//}
			//finally
			//{
			//	this.typeContext.Pop();
			//}
		}

		public override Expression Visit( Expression node )
		{
			return base.Visit( node );
		}

		protected override Expression VisitLambda<T>( Expression<T> node )
		{
			if ( !node.ReturnType.IsClass
				|| node.ReturnType.Equals( typeof( string ) ) )
			{
				this.gets.Add( k => k );
				return node;
			}
			else
			{
				return base.VisitLambda<T>( node );
			}
		}

		//protected override Expression VisitParameter( ParameterExpression node )
		//{
		//	if ( !this.memberContext.IsEmpty() )
		//	{
		//		Type keyType = typeof( TKey );

		//		ParameterExpression param = Expression.Parameter( keyType );
		//		Expression getter = param;

		//		while ( !this.memberContext.IsEmpty() )
		//		{
		//			var member = this.memberContext.Pop();
		//			var property = keyType.GetProperty( member.Member.Name );

		//			if ( property != null )
		//			{
		//				getter = Expression.MakeMemberAccess( getter, property );
		//				keyType = member.Type;
		//			}
		//			else
		//			{
		//				// Se não encontrou a propriedade, é porque deve ter sido renomeada
		//			}
		//		}

		//		this.gets.Add( Expression.Lambda<Func<TKey, object>>( Expression.Convert( getter, typeof( object ) ), param ).Compile() );
		//	}
		//	return node;
		//}

		//protected override Expression VisitMember( MemberExpression node )
		//{
		//	//this.gets.Add(Expression.Lambda<Func<TKey, object>>(node, 
		//	// return base.VisitMember( node );

		//	this.memberContext.Push( node );
		//	try
		//	{
		//		return base.VisitMember( node );
		//	}
		//	finally
		//	{
		//		Debug.Assert( this.memberContext.Count == 0, "A chave é inválida." );
		//	}
		//	//ParameterExpression p = node.Find<ParameterExpression>();
		//	//this.gets.Add( Expression.Lambda<Func<TKey, object>>( node, p ).Compile() );
		//	//return node;
		//}
	}
}
